
// This file is part of the mstp-lib library, available at http://code.google.com/p/mstp-lib/ 
// Copyright (c) 2011-2014 Adrian Gostin, distributed under the GNU General Public License v3.

#define _CRT_SECURE_NO_WARNINGS

#include "log.h"
#include "bridge.h"
#include "assert.h"

#include <stdarg.h>
#include <stdio.h>

#ifdef _MSC_VER
	#define snprintf _snprintf
#endif

void STP_FlushLog (BRIDGE* bridge)
{
	assert (bridge->logBufferUsedSize < bridge->logBufferSize);

	bridge->logBuffer [bridge->logBufferUsedSize] = 0;
	bridge->callbacks.debugStrOut (bridge, bridge->logCurrentPort, bridge->logCurrentTree, bridge->logBuffer, true);
	bridge->logBufferUsedSize = 0;
}

static void WriteChar1 (BRIDGE* bridge, int port, int tree, char c)
{
	if ((bridge->logBufferUsedSize == bridge->logBufferSize - 1)
		|| (port != bridge->logCurrentPort) || (tree != bridge->logCurrentTree))
	{
		if (bridge->logBufferUsedSize > 0)
		{
			bridge->logBuffer [bridge->logBufferUsedSize] = 0;
			bridge->callbacks.debugStrOut (bridge, bridge->logCurrentPort, bridge->logCurrentTree, bridge->logBuffer, false);
			bridge->logBufferUsedSize = 0;
		}

		bridge->logCurrentPort = port;
		bridge->logCurrentTree = tree;
	}

	bridge->logBuffer [bridge->logBufferUsedSize] = c;
	bridge->logBufferUsedSize++;	
}

static void WriteChar (BRIDGE* bridge, int port, int tree, char c)
{
	WriteChar1 (bridge, port, tree, c);

	if (c == '\n')
	{
		for (unsigned int i = 0; i < bridge->logIndent; i++)
			WriteChar1 (bridge, port, tree, ' ');
		
		bridge->logLineStarted = false;
	}
	else
		bridge->logLineStarted = true;
}


void STP_Indent (BRIDGE* bridge)
{
	bridge->logIndent += BRIDGE::LogIndentSize;

	if (bridge->logLineStarted == false)
	{
		for (unsigned int i = 0; i < BRIDGE::LogIndentSize; i++)
			WriteChar1 (bridge, bridge->logCurrentPort, bridge->logCurrentTree, ' ');
	}
}

void STP_Unindent (BRIDGE* bridge)
{
	assert (bridge->logIndent >= BRIDGE::LogIndentSize); // Trying to Unindent() more times than Indent()-ed.
	
	bridge->logIndent -= BRIDGE::LogIndentSize;
	
	if (bridge->logLineStarted == false)
	{
		for (unsigned int i = 0; i < BRIDGE::LogIndentSize; i++)
			WriteChar1 (bridge, bridge->logCurrentPort, bridge->logCurrentTree, '\x08');
	}
}

void STP_Log (BRIDGE* bridge, int port, int tree, const char* format, ...)
{
	char _buffer [10];

    va_list ap;
    va_start (ap, format);

	while (*format != 0)
	{
		if (strncmp (format, "{BID}", 5) == 0)
		{
			va_arg (ap, BRIDGE_ID*)->Dump (bridge, port, tree);
			format += 5;
		}
		else if (strncmp (format, "{PID}", 5) == 0)
		{
			va_arg (ap, PORT_ID*)->Dump (bridge, port, tree);
			format += 5;
		}
		else if (strncmp (format, "{BA}", 4) == 0)
		{
			const unsigned char* a = va_arg (ap, unsigned char*);
			STP_Log (bridge, port, tree, "{X2}{X2}{X2}{X2}{X2}{X2}", a[0], a[1], a[2], a[3], a[4], a[5]);
			format += 4;
		}
		else if (strncmp (format, "{PVS}", 5) == 0)
		{
			va_arg (ap, PRIORITY_VECTOR*)->Dump (bridge, port, tree);
			format += 5;
		}
		else if (strncmp (format, "{S", 2) == 0)
		{
			format += 2;
			size_t size = 0;
			while (true)
			{
				char ch = *format;
				if ((ch < '0') || (ch > '9'))
					break;
				
				size = 10 * size + ch - '0';
				format++;
			}
			
			assert (*format == '}');
			format++;
			
			const char* str = va_arg (ap, const char*);
			size_t strLen = strlen (str);
			
			size_t paddingSize = (strLen >= size) ? 0 : (size - strLen);
			for (size_t i = 0; i < paddingSize; i++)
				WriteChar (bridge, port, tree, ' ');

			STP_Log (bridge, port, tree, str);
		}
		else if (strncmp (format, "{T}", 3) == 0)
		{
			unsigned int v = va_arg (ap, unsigned int);
			STP_Log (bridge, port, tree, "{D}.{D3}", (int) (v / 1000), (int) (v % 1000));
			format += 3;
		}
		/*
		else if (strncmp (format, "{I}", 3) == 0)
		{
			int i = va_arg (ap, int);
			while (i--)
				WriteChar (bridge, port, tree, ' ');
			
			format += 3;
		}
		*/
		else if (strncmp (format, "{TN}", 4) == 0)
		{
			int i = va_arg (ap, int);
			if (i == 0)
				STP_Log (bridge, port, tree, "CIST");
			else
				STP_Log (bridge, port, tree, "MST{D}", i);

			format += 4;
		}
		else if (strncmp (format, "{TMS}", 5) == 0)
		{
			const TIMES* times = va_arg (ap, TIMES*);
			STP_Log (bridge, port, tree, "MessageAge={D}, MaxAge={D}, HelloTime={D}, FwDelay={D}, remainingHops={D}",
					  times->MessageAge, times->MaxAge, times->HelloTime, times->ForwardDelay, times->remainingHops);
			format += 5;
		}
		else if (strncmp (format, "{D", 2) == 0)
		{
			format += 2;
			int size;
			if (*format >= '0' && *format <= '9')
			{
				size = *format - '0';
				format++;
			}
			else
				size = 0;

			assert (*format == '}');
			format++;

			int v = va_arg (ap, int);

			snprintf (_buffer, sizeof (_buffer), "%0*d", size, v);

			for (unsigned int i = 0; i < sizeof (_buffer) && _buffer [i] != 0; i++)
				WriteChar (bridge, port, tree, _buffer [i]);
		}
		else if (strncmp (format, "{X", 2) == 0)
		{
			format += 2;
			int size;
			if (*format >= '0' && *format <= '9')
			{
				size = *format - '0';
				format++;
			}
			else
				size = 0;

			assert (*format == '}');
			format++;

			int v = va_arg (ap, int);

			snprintf (_buffer, sizeof (_buffer), "%0*x", size, v);

			for (unsigned int i = 0; i < sizeof (_buffer) && _buffer [i] != 0; i++)
				WriteChar (bridge, port, tree, _buffer [i]);
		}
		else if (*format == '{')
		{
			assert (false); // not implemented
		}
		else
		{
			WriteChar (bridge, port, tree, *format);
			format++;
		}
	}

	va_end (ap);
}
